/************************************************************************
Copyright 2003, 2004, 2005, 2006, 2007, 2008 Grid Technology Research Center,
National Institute of Advanced Industrial Science and Technology.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************/

package jp.aist.gtrc.plus.reserve;

import java.util.Calendar;
import java.util.Collection;
import java.util.TreeMap;

public class ReserveTable {

	private class ReserveInfoKey implements Comparable<ReserveInfoKey> {

		private final Calendar time;
		private final boolean timeReverse;
		private final ReserveId id;

		ReserveInfoKey(Calendar time, boolean timeReverse) {
			this(time, timeReverse, null);
		}

		ReserveInfoKey(Calendar time, boolean timeReverse, ReserveId id) {
				this.time = time;
				this.timeReverse = timeReverse;
				this.id = id;
		}

		public boolean equals(Object o) {
			if (o instanceof ReserveInfoKey) {
				return (compareTo((ReserveInfoKey)o) == 0);
			} else {
				return false;
			}
		}

		public final int hashCode() {
			return time.hashCode();
		}

		public int compareTo(ReserveInfoKey o) {
			ReserveInfoKey other = (ReserveInfoKey)o;
			int comp = this.time.compareTo(other.time);
			if (comp == 0) {
				if (this.id != null && other.id != null) {
					comp = this.id.compareTo(other.id);
				}
			} else if (timeReverse) {
				comp = -comp;
			}
			return comp;
		}

	}

	private class ReserveInfoStartKey extends ReserveInfoKey {
		ReserveInfoStartKey(ReserveInfo info) {
			super(info.getStartTime(), false, info.getReserveId());
		}
		ReserveInfoStartKey(Calendar start) {
			super(start, false);
		}
	}

	private class ReserveInfoEndKey extends ReserveInfoKey {
		ReserveInfoEndKey(ReserveInfo info) {
			super(info.getEndTime(), true, info.getReserveId());
		}
		ReserveInfoEndKey(Calendar end) {
			super(end, true, ReserveId.getFirstInstance());
		}
	}


	private final TreeMap<ReserveId, ReserveInfo> idMap;
	private final TreeMap<ReserveInfoStartKey, ReserveInfo> startTimeMap;
	private final TreeMap<ReserveInfoEndKey, ReserveInfo> endTimeMap;

	ReserveTable() {

		// NOTE: use TreeMap for 'idMap' to keep order
		// even if aborting destruction of reserve.
		idMap = new TreeMap<ReserveId, ReserveInfo>();
		startTimeMap = new TreeMap<ReserveInfoStartKey, ReserveInfo>();
		endTimeMap = new TreeMap<ReserveInfoEndKey, ReserveInfo>();

	}

	synchronized void put(ReserveInfo info) {

		if (containsId(info.getReserveId()) == false) {
			idMap.put(info.getReserveId(), info);
			startTimeMap.put(new ReserveInfoStartKey(info), info);
			endTimeMap.put(new ReserveInfoEndKey(info), info);
		} else {
			assert(false);
		}

	}

	synchronized void remove(ReserveInfo info) {

		if (containsId(info.getReserveId()) == true) {
			idMap.remove(info.getReserveId());
			startTimeMap.remove(new ReserveInfoStartKey(info));
			endTimeMap.remove(new ReserveInfoEndKey(info));
		}

	}

	synchronized void reput(ReserveInfo info) {

		remove(info);
		put(info);

	}

	synchronized void clear() {

		idMap.clear();
		startTimeMap.clear();
		endTimeMap.clear();

	}

	public synchronized ReserveInfo get(ReserveId rsvId) {

		if (rsvId != null) {
			return idMap.get(rsvId);
		} else {
			return null;
		}

	}

	public synchronized Collection<ReserveInfo> getAllById() {

		return idMap.values();

	}

	public synchronized int size() {

		return idMap.size();

	}

	synchronized boolean containsId(ReserveId rsvId) {

		return idMap.containsKey(rsvId);

	}

	synchronized Collection<ReserveInfo> getAllByStartOrder() {

		return startTimeMap.values();

	}

	synchronized Collection<ReserveInfo> getAfterStartTime(Calendar start, boolean equalTimeAlso) {

		Calendar c;
		if (equalTimeAlso) {
			// returns ReserveInfo which startTime >= c == 'start' (including equal)
			c = start;
		} else {
			// returns ReserveInfo which startTime >= c > 'start' (NOT including equal)
			c = (Calendar)start.clone();
			c.add(Calendar.MILLISECOND, 1);
		}

		/*
		 * returns ReserveInfo which startTime >= 'c'
		 */
		return startTimeMap.tailMap(new ReserveInfoStartKey(c)).values();

	}

	synchronized Collection<ReserveInfo> getNotAfterEndTime(Calendar end) {

		/*
		 * returns ReserveInfo which endTime <= 'end' (including equal)
		 *
		 * NOTE: 'endTimeMap' is sorted by reverse order.
		 * Assume we have reserves which endTimes are 10:00, 11:00, 12:00.
		 * endTimeMap.values() is reserve list of { 12:00, 11:00, 10:00 }.
		 * endTimeMap.tailMap(11:00) is reserve list of { 11:00, 10:00 },
		 * that is what we want to get.
		 */
		return endTimeMap.tailMap(new ReserveInfoEndKey(end)).values();

	}

}
